﻿namespace wToggle.Platform
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net.Http;
    using System.Reflection;
    using System.Runtime.Serialization.Json;
    using System.Text;
    using System.Threading.Tasks;

    using wToggle.Platform.Data;
    using wToggle.Platform.Interfaces;

    public static class Extensions
    {
        public static async Task<TResult> PostJsonAsync<TResult, TData>(this HttpClient client, string requestUri, TData data)
        {
            if (!Toggly.HasInternetAccess())
            {
                return default(TResult);
            }

            var serializer = new DataContractJsonSerializer(typeof(TData), new DataContractJsonSerializerSettings());
            var deserializer = new DataContractJsonSerializer(typeof(TResult));
            using (var ms = new MemoryStream())
            {
                serializer.WriteObject(ms, data);
                ms.Flush();
                var content = Encoding.UTF8.GetString(ms.ToArray(), 0, (int)ms.Length);
                var response = await client.PostAsync(requestUri, new StringContent(content, Encoding.UTF8, "application/json"));

                ms.SetLength(0); // reset stream

                try
                {
                    response.EnsureSuccessStatusCode();
                }
                catch (Exception e)
                {
                    return default(TResult);
                }

                var result = (TResult)deserializer.ReadObject(await response.Content.ReadAsStreamAsync());
                return result;
            }
        }

        public static byte[] ToASCII(this string s)
        {
            byte[] retval = new byte[s.Length];
            for (int ix = 0; ix < s.Length; ++ix)
            {
                char ch = s[ix];
                if (ch <= 0x7f) retval[ix] = (byte)ch;
                else retval[ix] = (byte)'?';
            }
            return retval;
        }

        public static async Task<TResult> PutJsonAsync<TResult, TData>(this HttpClient client, string requestUri, TData data)
        {
            if (!Toggly.HasInternetAccess())
            {
                return default(TResult);
            }

            var serializer = new DataContractJsonSerializer(typeof(TData), new DataContractJsonSerializerSettings());
            var deserializer = new DataContractJsonSerializer(typeof(TResult));
            using (var ms = new MemoryStream())
            {
                serializer.WriteObject(ms, data);
                ms.Flush();
                var content = Encoding.UTF8.GetString(ms.ToArray(), 0, (int)ms.Length);
                var response = await client.PutAsync(requestUri, new StringContent(content, Encoding.UTF8, "application/json"));

                ms.SetLength(0); // reset stream

                try
                {
                    response.EnsureSuccessStatusCode();
                }
                catch (Exception e)
                {
                    return default(TResult);
                }

                var result = (TResult)deserializer.ReadObject(await response.Content.ReadAsStreamAsync());
                return result;
            }
        }

        public static async Task<TResult> GetJsonAsync<TResult>(this HttpClient client, string requestUri) where TResult : class
        {
            if (!Toggly.HasInternetAccess())
            {
                return default(TResult);
            }

            var deserializer = new DataContractJsonSerializer(typeof(TResult));
            var response = await client.GetAsync(requestUri);
            try
            {
                response.EnsureSuccessStatusCode();
            }
            catch (HttpRequestException e)
            {
                return default(TResult);
            }

            var result = (TResult)deserializer.ReadObject(await response.Content.ReadAsStreamAsync());
            return result;
        }

        public static string ToDurationString(this TimeSpan span)
        {
            return string.Format("{0:00}:{1:00}:{2:00}", Math.Truncate(span.TotalHours), span.Minutes, span.Seconds);
        }

        public static CacheRecord<TEntity> FindKeyOrNull<TEntity>(this IDictionary<ulong, CacheRecord<TEntity>> dict, ulong id) where TEntity : class, IEntity
        {
            if (dict == null)
            {
                return null;
            }

            CacheRecord<TEntity> result;

            dict.TryGetValue(id, out result);
            return result;
        }

        public static TExpected GetAttributeValue<T, TExpected>(this Enum enumeration, Func<T, TExpected> expression)
            where T : Attribute
        {
            var attribute =
                enumeration.GetType()
                    .GetTypeInfo()
                    .DeclaredMembers.FirstOrDefault(member => member.Name == enumeration.ToString())
                    .GetCustomAttributes(typeof(T), false)
                    .Cast<T>()
                    .SingleOrDefault();

            return attribute == null ? default(TExpected) : expression(attribute);
        }
    }
}
